#include "scene.h"
#include "cell.h"
#include "algorithm.h"
#include <QBrush>
#include <QDebug>
#include <QRectF>
#include <QPointF>
#include <QPoint>
#include <QBrush>
#include <QColor>
#include <QList>
#include <QPainter>
#include <QGraphicsPixmapItem>
#include <QGraphicsSceneMouseEvent>

Scene::Scene(QObject *parent) :
    QGraphicsScene(parent)
{

    mPixelSize = 20;
    mUnderMouseItem = 0;
    mUnderMouseItemColor = QColor(102, 185, 227);
    mTempPoints = 0;
    mPointsIter = 0;
    mAlg = 0;
    isDebug = false;
    setBackgroundBrush(QBrush(QColor(255, 255, 215)));
    drawLines();
}

void Scene::setSceneRect(const QRectF &rect){
    QGraphicsScene::setSceneRect(rect);
    drawLines();
}

void Scene::clear(){
    mTempPoints->clear();
    foreach(QGraphicsItem* item, items()) {
        removeItem(item);
        delete item;
    }
    mUnderMouseItem = 0;
    drawLines();
}

void Scene::clearTemp(){
    if (mTempPoints){
        if (mAlg->isTemp())
            while(!mTempPoints->isEmpty()){
                Cell* cell = mTempPoints->takeFirst();
                removeItem(cell);
                delete cell;
            }
        else
            mTempPoints->clear();
    }
}

void Scene::drawLines(){
    int w = sceneRect().width();
    int h = sceneRect().height();
    QPen pen(QColor(186, 186, 186));
    for(int i=0; i<w; i+=mPixelSize){
        addLine(i, 0, i, h, pen);
    }
    for(int j=0; j<h; j+=mPixelSize){
        addLine(0, j, w, j, pen);
    }
}

void Scene::update(const QRectF &rect= QRectF()){
    QGraphicsScene::update(rect);
    drawLines();
}


void Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *event){
    int x = int(event->scenePos().x());
    int y = int(event->scenePos().y());
    QPoint posForAlg(x/mPixelSize, y/mPixelSize);
    QPoint pos(x/mPixelSize*mPixelSize, y/mPixelSize*mPixelSize);
    if (mAlg && !isDebug)
        drawPoints(mAlg->handleMoveEvent(posForAlg));
    if (!mUnderMouseItem){
        mUnderMouseItem = new Cell(mPixelSize, x, y, mUnderMouseItemColor);
        addItem(mUnderMouseItem);
    }
    mUnderMouseItem->setPos(pos);
}

void Scene::mousePressEvent(QGraphicsSceneMouseEvent *event){
    int x = int(event->scenePos().x());
    int y = int(event->scenePos().y());
    QPoint pos(x/mPixelSize, y/mPixelSize);
    if (mAlg)
        drawPoints(mAlg->handlePressEvent(pos));
}

void Scene::setAlg(Algorithm *alg){
    mAlg = alg;
}

inline int Scene::convert(int x){
    return x*mPixelSize;
}

void Scene::drawNext(){
    if (!isDebug || !mPointsIter)
        return;
    bool isSpecial = !mPointsIter->hasPrevious();
    if (mPointsIter->hasNext()){
        QPoint point = mPointsIter->next();
        isSpecial = isSpecial || !mPointsIter->hasNext();
        drawPoint(point, isSpecial);
    } else {
        emit drawed();
    }
}

void Scene::toDebug(){
    isDebug = !isDebug;
    clearTemp();
    if(mAlg)
        mAlg->drop();
}

void Scene::drawPointsAll(PointsIter *points){
    if (!mTempPoints)
        mTempPoints = new QList<Cell*>;
    clearTemp();
    if (!points)
        return;
    bool isSpecial = true;
    while(points->hasNext()){
        QPoint point = points->next();
        isSpecial = isSpecial || !points->hasNext();
        mTempPoints->append(drawPoint(point, isSpecial));
        isSpecial = false;
    }
    delete points;
}

Cell* Scene::drawPoint(QPoint point, bool isSpecial){
    Cell* tmpCell;
    if (isSpecial)
        tmpCell = new Cell(mPixelSize, point.x(), point.y(), Qt::green);
    else
        tmpCell = new Cell(mPixelSize, point.x(), point.y(), Qt::blue);
    addItem(tmpCell);
    tmpCell->setPos(convert(point.x()), convert(point.y()));
    return tmpCell;
}

void Scene::drawPointsDebug(PointsIter *points){
    static bool first = false;
    first = !first;
    if (first)
        clearTemp();
    if (mPointsIter)
        delete mPointsIter;
    mPointsIter = points;
    if (first && mPointsIter->hasNext()){
        if (mTempPoints){
            mTempPoints->clear();
        } else {
            mTempPoints = new QList<Cell*>;
        }
        qDebug() << "hoho";
        mTempPoints->append(drawPoint(mPointsIter->next(), false));
        qDebug() << "hoho";
    }
    else if (!first && mPointsIter->hasNext()){
        mPointsIter->toBack();
        mTempPoints->append(drawPoint(mPointsIter->previous(), false));
        mPointsIter->toFront();
    }


}

void Scene::drawPoints(PointsIter *points){
    if (!isDebug)
        drawPointsAll(points);
    else
        drawPointsDebug(points);
}
